NestJS 的数据库无关设计
NestJS 本身是数据库无关的框架。底层使用 Express 或 Fastify 提供 HTTP 服务,数据库层面则依赖 Node.js 的原生驱动(driver)来对接各种数据库。但官方并不推荐直接使用这些底层驱动,而是建议通过 ORM 库来进行数据库操作——核心原因就是 ORM 能够一套代码对接多种数据库,真正做到开箱即用(out of the box)。
在 NestJS 官方文档的 Databases 章节,以下 ORM/ODM 库被明确推荐:
| 库 | 类型 | 官方集成包 | 核心定位 |
|---|---|---|---|
| TypeORM | ORM | @nestjs/typeorm | 装饰器驱动的全功能 ORM,NestJS 默认推荐 |
| Prisma | ORM | prisma + 自行封装 | 类型安全的下一代 ORM,Schema-first |
| Mongoose | ODM | @nestjs/mongoose | MongoDB 专用,操作最全面 |
| MikroORM | ORM | @mikro-orm/nestjs | 轻量级,支持多种数据库 |
| Sequelize | ORM | @nestjs/sequelize | 老牌 ORM,基于 Promise |
| Knex.js | Query Builder | 无官方包 | SQL 查询构建器,灵活性最高 |
选择时需要在覆盖面、稳定性、扩展性、社区支持之间权衡——这和挑选任何 npm 第三方包的思路一致。
主流 ORM 对比分析
TypeORM:覆盖面最广
TypeORM 是 NestJS 生态中最成熟的 ORM 方案,采用装饰器(Decorator)定义实体类,与 TypeScript 的契合度很高。
支持的数据库类型(通过 DataSource 配置 type 字段切换):
| 数据库 | type 值 | 状态 |
|---|---|---|
| PostgreSQL | postgres | 稳定 |
| MySQL | mysql | 稳定 |
| MariaDB | mariadb | 稳定 |
| SQLite | sqlite / better-sqlite3 | 稳定 |
| SQL Server | mssql | 稳定 |
| Oracle | oracle | 可用 |
| SAP Hana | sap | 可用 |
| MongoDB | mongodb | Experimental(实验性) |
实体类定义示例:
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 100 })
name: string;
@Column({ unique: true })
email: string;
@OneToMany(() => Post, (post) => post.author)
posts: Post[];
}
typescript
MongoDB 的支持标记为 Experimental,且 driver 版本锁定在 5.x。当前 MongoDB 已经到 7.x 和 8.x 版本,这意味着 TypeORM 无法使用 MongoDB 6.x+ 的新特性。GitHub Issues 中与 MongoDB 相关的问题超过 1700 个,生产环境使用需格外谨慎。
Prisma:类型安全的现代方案
Prisma 采用 Schema-first 的设计思路,通过 .prisma 文件定义数据模型,自动生成类型安全的 Client。
支持的数据库及版本要求:
| 数据库 | 最低版本 | 连接字符串格式 |
|---|---|---|
| PostgreSQL | 9.6+ | postgresql://user:pass@host:5432/db |
| MySQL | 5.6+ | mysql://user:pass@host:3306/db |
| MariaDB | 10.0+ | mysql://user:pass@host:3306/db |
| SQLite | 所有版本 | file:./dev.db |
| SQL Server | 2017+ | sqlserver://host:1433;database=db;... |
| MongoDB | 4.2+ | mongodb://user:pass@host:27017/db |
| CockroachDB | 21.2.4+ | postgresql://user:pass@host:26257/db |
Prisma Schema 定义(MongoDB 为例):
// prisma/schema.prisma
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
name String?
posts Post[]
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
}
prisma
Prisma 对 MongoDB 的支持有几个注意点:
| 特性 | 支持状态 | 说明 |
|---|---|---|
| 事务(Transactions) | 支持 | 副本集(Replica Set)模式下可用 |
| 索引(Indexes) | 支持 | 通过 @index 和 @@index 定义 |
| 数组/嵌套文档 | 支持 | String[]、Json 类型 |
| Aggregation Pipeline | 部分支持 | 需通过 aggregateRaw() 原生查询 |
| 联表查询 | 支持 | 通过 @relation 定义关系 |
对于复杂 MongoDB 查询,Prisma 提供了 aggregateRaw 和 $runCommandRaw 作为逃生舱:
// Prisma MongoDB 聚合查询
const result = await prisma.user.aggregateRaw({
pipeline: [
{ $match: { status: 'active' } },
{ $group: { _id: '$department', total: { $sum: 1 } } },
{ $sort: { total: -1 } },
],
});
typescript
Mongoose:MongoDB 的最佳拍档
Mongoose 虽然严格来说是 ODM(Object Document Mapping)而非 ORM,但在 MongoDB 操作上远比 TypeORM 和 Prisma 全面:
// Mongoose Schema 定义
import { Schema, model } from 'mongoose';
const userSchema = new Schema(
{
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
profile: {
about: String,
education: String,
career: String,
},
photos: [{ url: String, caption: String }],
},
{ timestamps: true },
);
export const User = model('User', userSchema);
typescript
Mongoose 的核心优势在于对 MongoDB 高级特性的全面支持:
// 完整的聚合管道支持
const report = await Sale.aggregate([
{ $match: { date: { $gte: new Date('2025-01-01') } } },
{
$group: {
_id: '$category',
totalRevenue: { $sum: '$amount' },
avgOrder: { $avg: '$amount' },
},
},
{ $sort: { totalRevenue: -1 } },
{ $limit: 10 },
]);
// 中间件钩子
userSchema.pre('aggregate', function () {
this.pipeline().unshift({ $match: { isDeleted: { $ne: true } } });
});
typescript
| 特性 | Mongoose | Prisma | TypeORM |
|---|---|---|---|
| MongoDB 版本支持 | 全版本 | 4.2+ | 5.x driver(实验性) |
| Aggregation Pipeline | 完整支持 | 需 aggregateRaw | 基础支持 |
| 中间件/Hook | 丰富 | 有限 | 支持 |
| 虚拟字段 | 支持 | 不支持 | 不支持 |
| Schema 验证 | 内置 | 内置 | 需额外配置 |
| 类型安全 | TypeScript 支持 | 自动生成(最强) | 装饰器 |
选择决策框架
三要素判断法
在通用模板项目中选择 ORM 时,围绕三个核心问题决策:
1. 是否需要对接 MongoDB?
└─ 否 → 选择 Prisma(覆盖绝大多数关系型数据库场景)
└─ 是 ↓
2. MongoDB 的版本是多少?
└─ 4.2+ 且不需要进阶特性 → Prisma 也能胜任
└─ 老版本(4.0 及以下)或需要进阶特性 ↓
3. 是否需要 MongoDB 进阶特性(聚合管道、复杂查询)?
└─ 需要 → 使用 Mongoose
└─ 不需要 → Prisma 可满足
text
推荐方案
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 纯关系型数据库(MySQL/PostgreSQL) | Prisma | 类型安全、迁移工具完善、开发体验好 |
| 关系型 + 简单 MongoDB | Prisma | 一套 Schema 管理两种数据库 |
| MongoDB 为主 | Mongoose | 特性覆盖最全,社区最成熟 |
| 需兼容最多数据库类型 | TypeORM | 支持数据库种类最多(含商业数据库) |
| 全场景覆盖(模板项目) | Prisma + Mongoose | 关系型用 Prisma,MongoDB 进阶用 Mongoose |
多数据库集成的代码架构
当同一个项目需要同时对接多种数据库时,NestJS 官方文档的 Multiple Databases 章节提供了架构指导。
目录结构
src/
├── common/
│ └── database/
│ ├── prisma/ # Prisma 模块(关系型数据库)
│ │ ├── prisma.module.ts
│ │ └── prisma.service.ts
│ └── mongoose/ # Mongoose 模块(MongoDB)
│ ├── mongoose.module.ts
│ └── schemas/
│ └── user.schema.ts
├── modules/
│ ├── user/
│ │ ├── user.entity.ts # TypeORM 实体(如果用 TypeORM)
│ │ └── user.schema.ts # Mongoose Schema(如果用 MongoDB)
│ └── ...
text
代价与权衡
同时集成 Prisma + Mongoose + TypeORM 三套方案会带来三个实际问题:
| 问题 | 影响 | 缓解措施 |
|---|---|---|
| 代码量翻倍 | 同一个业务实体需要定义多套模型(Prisma Schema / TypeORM Entity / Mongoose Schema) | 业务代码只实现一套,其他方案参考官方文档扩展 |
| 心智负担 | 切换上下文时容易混淆不同 ORM 的 API | 按模块严格划分,每个模块只用一种 ORM |
| 维护成本 | 三套 ORM 的升级和兼容性需要分别关注 | 业务层只维护主力方案,其他方案按需扩展 |
实际项目建议
大多数业务系统只需对接一种数据库(关系型或 MongoDB),只有以下特殊场景才需要多数据库支持:
- 跨系统升级 — 从老旧系统读取数据,数据库类型和版本各异
- 低代码/多租户平台 — 用户自带数据库,平台需要兼容用户环境
- 混合存储 — 关系型数据库存核心业务数据,MongoDB 存日志/文档等非结构化数据
通用模板项目的定位是为后续业务开发提供基础骨架,不是要覆盖所有数据库类型。投入产出比才是核心考量——绝大多数场景下选择一种 ORM 方案深入使用,远比浅尝辄止地集成三套方案更实用。
NestJS 官方集成包一览
NestJS 为推荐的数据库方案都提供了专门的集成包,简化了模块注册和依赖注入:
# TypeORM 集成
pnpm add @nestjs/typeorm typeorm
# Mongoose 集成
pnpm add @nestjs/mongoose mongoose
# Prisma(无官方集成包,需自行封装服务)
pnpm add prisma @prisma/client
bash
| 集成包 | 模块注册方式 | 特色功能 |
|---|---|---|
@nestjs/typeorm | TypeOrmModule.forRoot() / forRootAsync() | 自动注入 Repository,支持多数据源 |
@nestjs/mongoose | MongooseModule.forRoot() / forRootAsync() | 自动注册 Schema,Model 注入 |
| Prisma(自封装) | 自定义 Provider | 需手动封装 PrismaService,但类型安全最强 |
选择哪一种并不影响 NestJS 的核心架构,因为它们都通过 DI(依赖注入)系统提供服务实例。后续章节将分别演示 Prisma、TypeORM 和 Mongoose 的集成过程,业务代码选择其中一种方案深入展开。
↑